ULedger Virtual Machine
La ULedger Virtual Machine (ULVM) te permite escribir smart contracts. Los contratos se compilan a WebAssembly (WASM), y la ULVM expone un pequeño conjunto de funciones host que tu contrato puede invocar para:
- leer/escribir el estado del blockchain (almacenamiento clave–valor)
- registrar mensajes
- emitir eventos
- acceder al contexto del contrato (llamante, propietario, altura de bloque)
Modelo de estado: almacenamiento clave–valor
ULedger almacena el estado del contrato como un mapa clave–valor en el Blockchain. Tu contrato lee y escribe valores por clave.
Conceptualmente, el estado del contrato se ve así:
{
"state": {
"key1": "value1",
"key2": "value2"
}
}
Entonces, cuando llamas a set_storage("my-key", someValue).
Tipos de datos soportados
Dado que el estado se serializa, la ULVM soporta un conjunto fijo de tipos (a través del serializador que codifica/decodifica valores):
Null = 0,
Bool = 1,
Int32 = 2,
Int64 = 3,
String = 4,
Bytes = 5,
Array = 6,
Map = 7,
Tus métodos auxiliares (como
storeInt,retrieveInt, etc.) utilizan este serializador internamente para que no tengas que codificar/decodificar bytes manualmente.
Funciones host de la ULVM (imports)
En AssemblyScript, las funciones host de la ULVM son (a alto nivel):
get_storage: obtiene los bytes del valor para una claveset_storage: almacena bytes de valor bajo una clavedelete_storage: elimina una clave del estadohas_key: verifica si existe una clavelog: escribe un mensaje de registroemit_event: emite un evento con un nombre y un payload de datosget_caller: la cuenta/wallet que está llamando a este contratoget_owner: el propietario del contratoget_block_height: altura de bloque actual
Estas funciones toman punteros (
usize) porque WASM pasa datos a través de memoria lineal. En la práctica, normalmente llamarás a utilidades envolventes (comostoreInt) que gestionan punteros y serialización por ti.
Por qué tipado fuerte (AssemblyScript)
Tus contratos se escriben en AssemblyScript (sintaxis similar a TypeScript compilada a WASM). Debido a que compila a WebAssembly, usarás tipos explícitos como i32, i64, bool, Array<i32>, etc.
Tutorial: Almacenar y recuperar (ejemplo básico)
Este primer ejemplo muestra el flujo más sencillo:
- almacenar un
i32bajo una clave - recuperarlo
Paso 1: importar los helpers de la ULVM
import {
storeInt,
retrieveInt,
storeIntArray,
retrieveIntArray,
} from "../src/ulvm";
Estos helpers son envoltorios delgados alrededor de env.get_storage / env.set_storage, más la serialización.
Paso 2: escribir el contrato
// The entry file of your WebAssembly module.
import {
storeInt,
retrieveInt,
storeIntArray,
retrieveIntArray,
} from "../src/ulvm";
/**
* Simple direct test function that stores a value and retrieves it.
* Uses a fixed key to keep the example minimal.
*/
export function testStoreAndRetrieve(value: i32): i32 {
const key = "test-key";
// Store the value
storeValue(key, value);
// Retrieve and return the value
return retrieveValue(key);
}
/**
* Store a value in storage with a string key
*/
export function storeValue(key: string, value: i32): void {
storeInt(key, value);
}
/**
* Retrieve a value from storage by string key
*/
export function retrieveValue(key: string): i32 {
return retrieveInt(key);
}
/**
* Test storing and retrieving an array
*/
export function testStoreAndRetrieveArray(): i32 {
const key = "array-key";
const array = [10, 20, 30, 40, 50];
storeArray(key, array);
const retrieved = retrieveArray(key);
// Return the length to verify it worked
return retrieved.length;
}
/**
* Store an array with a string key
*/
export function storeArray(key: string, values: Array<i32>): void {
storeIntArray(key, values);
}
/**
* Retrieve an array by string key
*/
export function retrieveArray(key: string): Array<i32> {
return retrieveIntArray(key);
}
Qué hace este contrato
testStoreAndRetrieve(value: i32) -> i32
- escribe
valueen el almacenamiento bajo"test-key" - lee
"test-key"de vuelta - devuelve el valor recuperado
Entonces, si llamas a testStoreAndRetrieve(123), tu estado termina conceptualmente así:
{
"state": {
"test-key": 123
}
}
testStoreAndRetrieveArray() -> i32
- almacena
[10,20,30,40,50]bajo"array-key" - lo recupera de vuelta
- devuelve la longitud del array (
5)
Compilación: compilar AssemblyScript a WebAssembly
Si estás usando la estructura de proyecto estándar de AssemblyScript, el flujo típico es:
1) Instalar AssemblyScript (dependencia de desarrollo)
npm i -D assemblyscript
2) Coloca el punto de entrada de tu contrato en assembly/index.ts
Por ejemplo:
assembly/smart-contract.ts(tu archivo de contrato)
3) Compilar a .wat y .wasm
Opción A: comando directo asc
npx asc assembly/smart-contract.ts -o smart-contract.wasm --textFile smart-contract.wat
Resultado: tendrás un archivo .wat (ejemplo: smart-contract.wat) que es un archivo de texto que el SDK de ULedger puede desplegar.
Ejecución: Despliegue
Para desplegar el Smart Contract compilado puedes usar el SDK de ULedger.
Para la guía en Go sobre cómo hacer esto, consulta la documentación de Despliegue de Smart Contracts.